# Migrating to v2

## What's new

### New [`wrap`](/docs/react/wrap) builder [#270](https://github.com/toss/suspensive/pull/270)

A new feature that wraps a component in `<Suspense/>`, `<ErrorBoundary/>`, and `<ErrorBoundaryGroup/>` all at once.

For `<Suspense/>`, `<ErrorBoundary/>`, `<ErrorBoundaryGroup/>`, etc. many people use HOC to wrap these around a component. This is because these components require some processing on their children. So, in order to not unnecessarily divide components and create depth, we use the HOCs for each interface, withErrorBoundary, withErrorBoundaryGroup, and withSuspense, but as we often use each HOC in combination, we also need to improve readability. To improve this, we decided to provide wrap.

```jsx
import { wrap } from '@suspensive/react'
import { useSuspenseQuery } from '@suspensive/react-query'

const Example = wrap
  .ErrorBoundaryGroup({ blockOutside: false })
  .ErrorBoundary({
    fallback: ({ error }) => <>{error.message}</>,
    onError: logger.log,
  })
  .Suspense({ fallback: <>loading...</>, clientOnly: true })
  .on(() => {
    const query = useSuspenseQuery({
      queryKey: ['key'],
      queryFn: () => api.text(),
    })
    return <>{query.data.text}</>
  })
```

### New `shouldCatch` prop of `<ErrorBoundary/>` [#569](https://github.com/toss/suspensive/pull/569)

Suspensive's `<ErrorBoundary/>` can catch all thrown errors that occur in children. However, because it catches all thrown errors, when using `<ErrorBoundary/>`, I thought about placing `<ErrorBoundary/>` in a narrower position.
For this reason, we added a new prop called shouldCatch to ErrorBoundary, which allows you to set which Error should be caught.

![ErrorBoundary shouldCatch example](/img/errorBoundary-shouldcatch-prop.png)

1. `shouldCatch`: ErrorConstructor

```jsx
import { ErrorBoundary } from '@suspensive/react'

class CustomError extends Error {}

const Example = () => {
  return (
    <ErrorBoundary fallback={({ error }) => <>RestError: {error.message}</>}>
      <ErrorBoundary
        shouldCatch={CustomError}
        onError={logOnCustomError}
        fallback={({ error }) => <>CustomError: {error.message}</>}
      >
        <ThrowErrorComponent />
      </ErrorBoundary>
    </ErrorBoundary>
  )
}
```

2. `shouldCatch`: callback

```jsx
import { ErrorBoundary } from '@suspensive/react'

class CustomError extends Error {}

const Example = () => {
  return (
    <ErrorBoundary fallback={({ error }) => <>RestError: {error.message}</>}>
      <ErrorBoundary
        shouldCatch={(error) => error instanceof CustomError}
        onError={logOnCustomError}
        fallback={({ error }) => <>CustomError: {error.message}</>}
      >
        <ThrowErrorComponent />
      </ErrorBoundary>
    </ErrorBoundary>
  )
}
```

3. `shouldCatch`: boolean

```jsx
import { ErrorBoundary } from '@suspensive/react'

class CustomError extends Error {}

const Example = () => {
  return (
    <ErrorBoundary fallback={({ error }) => <>RestError: {error.message}</>}>
      <ErrorBoundary
        shouldCatch={new Date().toISOString() > '2024-01-01T00:00:00.000Z'}
        onError={logOnErrorAfter2024}
        fallback={({ error }) => <>ErrorAfter2024: {error.message}</>}
      >
        <ThrowErrorComponent />
      </ErrorBoundary>
    </ErrorBoundary>
  )
}
```

### New [`<ErrorBoundary.Consumer/>`](/docs/react/ErrorBoundary), [`<ErrorBoundaryGroup.Consumer/>`](/docs/react/ErrorBoundaryGroup) [#610](https://github.com/toss/suspensive/pull/610)

These components can be used to use `useErrorBoundary`, `useErrorBoundaryGroup` in jsx inlinely.

```jsx
import { ErrorBoundary, ErrorBoundaryGroup } from '@suspensive/react'

const Example = () => {
  return (
    <ErrorBoundaryGroup>
      <ErrorBoundaryGroup.Consumer>
        {({ reset }) => <button onClick={reset}>reset all</button>}
      </ErrorBoundaryGroup.Consumer>
      <ErrorBoundary fallback={({ error }) => <>{error.message}</>}>
        <ErrorBoundary.Consumer>
          {({ setError }) => (
            <button onClick={() => setError(new Error('error message'))}>
              setError
            </button>
          )}
        </ErrorBoundary.Consumer>
      </ErrorBoundary>
    </ErrorBoundaryGroup>
  )
}
```

## Handling BREAKING CHANGES

### Removed `<AsyncBoundary/>`

We removed `<AsyncBoundary/>` in v2. [#295](https://github.com/toss/suspensive/issues/295)

Because `<AsyncBoundary/>` uses `<ErrorBoundary/>` internally, it can be used with `useErrorBoundary` and is affected by `<ErrorBoundaryGroup/>`. We decided to remove this component from v2, believing that it would be better for maintainability and interface unification for library users.

`<AsyncBoundary/>`'s feature is just wrap two component(`<Suspense/>`, `<ErrorBoundary/>`) by one.
So, you can split by two like this.

```diff
+ import { Suspense, ErrorBoundary } from '@suspensive/react'
- import { AsyncBoundary } from '@suspensive/react'

+ <ErrorBoundary fallback={<Error />} onError={onError} onReset={onReset}>
+   <Suspense fallback={<Loading />}>
+     <Children />
+   </Suspense>
+ </ErrorBoundary>
- <AsyncBoundary pendingFallback={<Loading />} rejectedFallback={<Error />} onError={onError} onReset={onReset}>
-   <Children />
- </AsyncBoundary>
```

### Removed `withSuspense`, `withDelay`, `withErrorBoundary`, `withErrorBoundaryGroup`

These all HOCs can be replaced beautifully by new HOC builder [`wrap`](/docs/react/wrap) in v2.

```diff
+ import { wrap } from '@suspensive/react'
- import { withSuspense, withErrorBoundary, withErrorBoundaryGroup } from '@suspensive/react'

+ const Example = wrap
+   .ErrorBoundaryGroup({ blockOutside: false })
+   .ErrorBoundary({ fallback: ({ error }) => <>{error.message}</>, onError: logger.log })
+   .Suspense({ fallback: <>loading...</>, clientOnly: true })
+   .on(() => {
+     const query = useSuspenseQuery({
+       queryKey: ['key'],
+       queryFn: () => api.text(),
+     })
+     return <>{query.data.text}</>
+   })
- const Example = withErrorBoundaryGroup(
-   withErrorBoundary(
-     withSuspense(
-       () => {
-         const query = useSuspenseQuery({
-           queryKey: ['key'],
-           queryFn: () => api.text(),
-         })
-         return <>{query.data.text}</>
-       },
-       { fallback: <>loading...</>, clientOnly: true }
-     ),
-     { fallback: ({ error }) => <>{error.message}</>, onError: logger.log }
-   ),
-   { blockOutside: false }
- )
```

```diff
+ import { wrap } from '@suspensive/react'
- import { withSuspense } from '@suspensive/react'

+ const Example = wrap
+   .Suspense({
+     fallback: <>loading...</>,
+     clientOnly: true,
+   })
+   .on(() => {
+     const query = useSuspenseQuery({
+       queryKey: ['key'],
+       queryFn: () => api.text(),
+     })
+     return <>{query.data.text}</>
+   })
- const Example = withSuspense(
-   () => {
-     const query = useSuspenseQuery({
-       queryKey: ['key'],
-       queryFn: () => api.text(),
-     })
-     return <>{query.data.text}</>
-   },
-   {
-     fallback: <>loading...</>,
-     clientOnly: true,
-   }
- )
```

### Removed `<ErrorBoundaryGroup.Reset/>`

`<ErrorBoundaryGroup.Reset/>` just use `useErrorBoundaryGroup` internally. so We thought that changing it to something like Context.Consumer would make the component's behavior easier to understand for React developers. We changed the name to `<ErrorBoundaryGroup.Consumer/>` and kept the interface the same.

```diff
import { ErrorBoundaryGroup } from '@suspensive/react'

const Example = () => {
  return (
    <ErrorBoundaryGroup>
-     <ErrorBoundaryGroup.Reset trigger={(group) => <button onClick={group.reset}>reset all</button>} />
+     <ErrorBoundaryGroup.Consumer>
+       {(group) => <button onClick={group.reset}>reset all</button>}
+     </ErrorBoundaryGroup.Consumer>
    </ErrorBoundaryGroup>
  )
}
```

### Rename `defaultOptions` → `defaultProps` of `Suspensive`

```diff
import { ErrorBoundaryGroup } from '@suspensive/react'

const suspensive = new Suspensive({
- defaultOptions: {
+ defaultProps: {
    suspense: {
      fallback: 'default loading...',
    },
  },
})
```

### Rename `<Suspense.CSROnly/>` → `<Suspense clientOnly/>` as prop

```diff
import { Suspense } from '@suspensive/react'

const Example = () => {
  return (
-   <Suspense.CSROnly fallback={<>loading...</>}>
+   <Suspense clientOnly fallback={<>loading...</>}>
      <>children</>
    </Suspense>
  )
}
```
